﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using sinetd.Services.Modbus;
using sinetd.Utilities;

namespace ModbusCLI
{
    class Program
    {
        static void Main(string[] args)
        {
            Console.Title = "Client ModBus [sinetd]";
            Console.BufferWidth = Console.LargestWindowWidth;

            int index = -1;

            sinetd.Protocols.IProtocol prot = sinetd.Protocols.Manager.Create("Serial");
            prot.Config = "port=COM2;TextMode=false";

            Console.WriteLine("Press any key to connect...");
            Console.ReadKey();

            prot.Connect();

            sinetd.Config cfg = new sinetd.Config();
            List<string[]> list;

            while (index != 0)
            {
                #region Getting data

                Console.Clear();

                do Console.Write("Choose DeviceID [0 - exit]: "); while (!int.TryParse(Console.ReadLine(), out index));
                if (index == 0) break;
                cfg.SetInt32("DeviceID", index);

                string line = "";
                do
                {
                    Console.Write("Choose Function Code [L - list, 0 - exit]: ");
                    line = Console.ReadLine();
                    if (line.Trim().ToUpper() == "L")
                    {
                        foreach (FunctionCodes fc in Enum.GetValues(typeof(FunctionCodes)))
                            Console.WriteLine(" {0,-2} - {1}", (int)fc, fc.ToString());
                    }
                } while (!int.TryParse(line, out index));
                if (index == 0) break;
                cfg.SetInt32("FunctionCode", index);

                do Console.Write("Choose Data1: "); while (!int.TryParse(Console.ReadLine(), out index));
                cfg.SetInt32("Data1", index);

                do Console.Write("Choose Data2: "); while (!int.TryParse(Console.ReadLine(), out index));
                cfg.SetInt32("Data2", index);

                index = -1;
                #endregion

                #region Print what frame will be contains

                FunctionCodes funcCode = (FunctionCodes)cfg.GetInt32("FunctionCode");
                cfg.SetString("FunctionCode", funcCode.ToString());

                list = new List<string[]>();
                list.Add(cfg.Keys.ToArray());
                list.Add(cfg.Values.ToArray());
                Console.WriteLine(list.PrintTable());

                #endregion

                #region Generating frame

                Frame f = new Frame((byte)cfg.GetInt32("DeviceID"), funcCode, cfg.GetInt32("Data1"), cfg.GetInt32("Data2"));

                byte[] frameOut = f.Encode();

                #endregion

                #region Print frame to send

                list = new List<string[]>();
                list.Add(frameOut.ToHexArray());
                Console.WriteLine(list.PrintTable());

                #endregion

                #region Sending frame

                Encoding enc = Encoding.GetEncoding(1252);
                string msg = enc.GetString(frameOut.ToArray());
                prot.Send(msg);

                #endregion

                #region Get answer frame

                System.Threading.Thread.Sleep(500);
                byte[] frameIn = enc.GetBytes(prot.Recv());

                #endregion

                #region Print answer frame

                list = new List<string[]>();
                list.Add(frameIn.ToHexArray());
                Console.WriteLine(list.PrintTable());

                #endregion

                list = new List<string[]>();
                if (frameIn.Length > 0)
                {
                    if (frameIn.Length > 5)
                    {
                        #region Readed memory

                        Console.WriteLine("Readed Memory");
                        if (funcCode == FunctionCodes.ReadCoilStatus || funcCode == FunctionCodes.ReadInputStatus)
                        {
                            
                            byte[] tmp = new byte[frameIn.Length - 5];
                            Array.Copy(frameIn, 3, tmp, 0, tmp.Length);

                            string[] tmpb = new string[tmp.Length * 8];
                            for (int i = 0; i < tmp.Length; i++)
                                for (int j = 0; j < 8; j++)
                                    tmpb[i * 8 + j] = Convert.ToString(tmp[i], 2).PadLeft(8, '0')[7 - j].ToString();

                            string[] num = new string[tmp.Length];
                            for (int i = 0; i < num.Length; i++) num[i] = (cfg.GetInt32("Data1") + i).ToString();

                            //list.Add(num);
                            list.Add(tmpb);
                        }
                        else if (funcCode == FunctionCodes.ReadHoldingRegisters || funcCode == FunctionCodes.ReadInputRegisters)
                        {
                            byte[] tmp = new byte[frameIn.Length - 5];
                            string[] num = new string[(tmp.Length / 2) + tmp.Length % 2];
                            for (int i = 0; i < num.Length; i++) num[i] = (cfg.GetInt32("Data1") + i).ToString();
                            Array.Copy(frameIn, 3, tmp, 0, tmp.Length);
                            list.Add(num);
                            list.Add(tmp.ToHexArray(2));
                        }

                        #endregion
                    }
                    else
                    {
                        Console.WriteLine("Error: 0x{0:X2} - {1}", frameIn[2], (ExceptionCodes)frameIn[2]);
                    }
                }

                Console.WriteLine(list.PrintTable());
                Console.ReadLine();
            }

            prot.Close();
        }
    }
}

